/**
 * 
 */
package org.vsg.stock.core.algorithm;

import java.io.PrintStream;

public class RegressionCalculator {
	private double[] x;
	private double[] y;
	private double sumX = 0.0D;
	private double sumY = 0.0D;
	private double sumXY = 0.0D;
	private double sumXsquared = 0.0D;
	private double sumYsquared = 0.0D;
	private double covariance = 0.0D;
	private double Sxx;
	private double Sxy;
	private double Syy;
	private double n;
	private double a = 0.0D;
	private double b = 0.0D;
	private int dataLength;
	private double[][] residual;
	private double maxAbsoluteResidual = 0.0D;
	private double SSR = 0.0D;
	private double SSE = 0.0D;
	private double sigmaHatSquared = 0.0D;
	private double minX = (1.0D / 0.0D);
	private double maxX = (-1.0D / 0.0D);
	private double minY = (1.0D / 0.0D);
	private double maxY = (-1.0D / 0.0D);

	public RegressionCalculator(double[] paramArrayOfDouble1,
			double[] paramArrayOfDouble2) {
		this.x = paramArrayOfDouble1;
		this.y = paramArrayOfDouble2;
		if (this.x.length != this.y.length)
			System.out.println("x, y vectors must be of same length");
		else
			this.dataLength = this.x.length;
		doStatistics();
	}

	private void doStatistics() {
		for (int i = 0; i < this.dataLength; i++) {
			this.minX = Math.min(this.minX, this.x[i]);
			this.maxX = Math.max(this.maxX, this.x[i]);
			this.minY = Math.min(this.minY, this.y[i]);
			this.maxY = Math.max(this.maxY, this.y[i]);
			this.sumX += this.x[i];
			this.sumY += this.y[i];
			this.sumXsquared += this.x[i] * this.x[i];
			this.sumYsquared += this.y[i] * this.y[i];
			this.sumXY += this.x[i] * this.y[i];
		}

		this.n = this.dataLength;
		this.Sxx = (this.sumXsquared - this.sumX * this.sumX / this.n);
		this.Syy = (this.sumYsquared - this.sumY * this.sumY / this.n);
		this.Sxy = (this.sumXY - this.sumX * this.sumY / this.n);
		this.b = (this.Sxy / this.Sxx);
		this.a = ((this.sumY - this.b * this.sumX) / this.n);
		this.SSR = (this.Sxy * this.Sxy / this.Sxx);
		this.SSE = (this.Syy - this.SSR);
		calculateResiduals();
	}

	private void calculateResiduals() {
		this.residual = new double[this.dataLength][];
		this.maxAbsoluteResidual = 0.0D;
		for (int i = 0; i < this.dataLength; i++) {
			this.residual[i] = new double[2];
			this.residual[i][0] = this.x[i];
			this.residual[i][1] = (this.y[i] - (this.a + this.b * this.x[i]));
			this.maxAbsoluteResidual = Math.max(this.maxAbsoluteResidual,
					Math.abs(this.y[i] - (this.a + this.b * this.x[i])));
		}
	}

	private void updateStatistics(double paramDouble1, double paramDouble2) {
		this.n += 1.0D;
		this.sumX += paramDouble1;
		this.sumY += paramDouble2;
		this.sumXsquared += paramDouble1 * paramDouble1;
		this.sumYsquared += paramDouble2 * paramDouble2;
		this.sumXY += paramDouble1 * paramDouble2;

		this.n = this.dataLength;
		this.Sxx = (this.sumXsquared - this.sumX * this.sumX / this.n);
		this.Syy = (this.sumYsquared - this.sumY * this.sumY / this.n);
		this.Sxy = (this.sumXY - this.sumX * this.sumY / this.n);
		this.b = (this.Sxy / this.Sxx);
		this.a = ((this.sumY - this.b * this.sumX) / this.n);
		this.SSR = (this.Sxy * this.Sxy / this.Sxx);
		this.SSE = (this.Syy - this.SSR);
		calculateResiduals();
	}

	public void reset() {
		this.x = new double[0];
		this.y = new double[0];
		this.dataLength = 0;
		this.n = 0.0D;
		this.residual = new double[0][];

		this.sumX = 0.0D;
		this.sumXsquared = 0.0D;
		this.sumY = 0.0D;
		this.sumYsquared = 0.0D;
		this.sumXY = 0.0D;
	}

	public double getIntercept() {
		return this.a;
	}

	public double getSlope() {
		return this.b;
	}

	public double[][] getResiduals() {
		return this.residual;
	}

	public double[] getDataX() {
		return this.x;
	}

	public double[] getDataY() {
		return this.y;
	}

	public void addPoint(double paramDouble1, double paramDouble2) {
		this.dataLength += 1;
		double[] arrayOfDouble1 = new double[this.dataLength];
		double[] arrayOfDouble2 = new double[this.dataLength];
		System.arraycopy(this.x, 0, arrayOfDouble1, 0, this.dataLength - 1);
		System.arraycopy(this.y, 0, arrayOfDouble2, 0, this.dataLength - 1);
		arrayOfDouble1[(this.dataLength - 1)] = paramDouble1;
		arrayOfDouble2[(this.dataLength - 1)] = paramDouble2;
		this.x = arrayOfDouble1;
		this.y = arrayOfDouble2;
		updateStatistics(paramDouble1, paramDouble2);
	}

	public double getMinX() {
		return this.minX;
	}

	public double getMaxX() {
		return this.maxX;
	}

	public double getMinY() {
		return this.minY;
	}

	public double getMaxY() {
		return this.maxY;
	}

	public double getMaxAbsoluteResidual() {
		return this.maxAbsoluteResidual;
	}

	public double getSxx() {
		return this.Sxx;
	}

	public double getSyy() {
		return this.Syy;
	}

	public double getSSR() {
		return this.SSR;
	}

	public double getSSE() {
		return this.SSE;
	}

	public double getMSE() {
		return this.SSE / (this.n - 2.0D);
	}

	public double getXBar() {
		return this.sumX / this.n;
	}

	public double getYBar() {
		return this.sumY / this.n;
	}

	public int getDataLength() {
		return this.x.length;
	}

	public double getPearsonR() {
		return this.Sxy / Math.sqrt(this.Sxx * this.Syy);
	}

	public double getSumXSquared() {
		return this.sumXsquared;
	}
}